package blockchain

import (
	"blockchain/utils"
	"bytes"
	"crypto/sha256"
	"fmt"
	"math"
	"math/big"
	"strings"
	"time"
)

const maxNonce = math.MaxInt64

type ProofOfWork struct {
	// 区块
	block  *Block
	target *big.Int
}

func NewProofOfWork(block *Block) *ProofOfWork {
	pow := ProofOfWork{
		block: block,
	}
	// 难度值先写死 不去推导 后面补充推导方式
	// hash值是256位 32个字节 16进制是64位
	targetStr := "00001" + strings.Repeat("0", 59)
	tmpBigInt := new(big.Int)
	tmpBigInt.SetString(targetStr, 16) // base 表示进制
	pow.target = tmpBigInt
	return &pow
}

func (pow *ProofOfWork) PrepareData(nonce uint64) []byte {
	b := pow.block
	tmp := [][]byte{
		utils.IntToHex(int64(b.Version)),
		b.PrevBlockHash,
		b.MerkleRoot,
		utils.IntToHex(int64(b.Timestamp)),
		utils.IntToHex(int64(b.Bits)),
		utils.IntToHex(int64(nonce)),
		// 不要把Hash拼进来 Hash是结果而不是输入
		b.Data,
	}
	return bytes.Join(tmp, []byte{})
}

func (pow *ProofOfWork) Run() ([]byte, uint64) {
	var hash [32]byte
	var nonce uint64 = 0
	start := time.Now()
	for nonce < maxNonce {
		data := pow.PrepareData(nonce)
		hash = sha256.Sum256(data)
		tmpInt := new(big.Int)
		tmpInt.SetBytes(hash[:])
		if tmpInt.Cmp(pow.target) == -1 {
			duration := time.Now().Sub(start)
			fmt.Printf("挖矿成功,耗时:%v, hash:%x, nonce:%d\n", duration, hash[:], nonce)
			break
		}
		nonce++
	}
	return hash[:], nonce
}

func (pow *ProofOfWork) IsValid() bool {
	//  区块的验证
	data := pow.PrepareData(pow.block.Nonce)
	hash := sha256.Sum256(data)

	tmpInt := new(big.Int)
	tmpInt.SetBytes(hash[:])

	//if tmpInt.Cmp(pow.target) == -1 {
	//	return true
	//}
	//return false

	return tmpInt.Cmp(pow.target) == -1
}
